package com.cdream.basic.classiccase.miaosha.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cdream.basic.mq.rocket.RocketMqTransProducer;
import com.cdream.basic.mybatisplus.service.UserService;
import com.cdream.basic.classiccase.miaosha.entity.Item;
import com.cdream.basic.classiccase.miaosha.entity.OrderInfo;
import com.cdream.basic.classiccase.miaosha.entity.SequenceInfo;
import com.cdream.basic.classiccase.miaosha.entity.StockLog;
import com.cdream.basic.classiccase.miaosha.mapper.OrderInfoMapper;
import com.cdream.basic.classiccase.miaosha.service.ItemService;
import com.cdream.basic.classiccase.miaosha.service.OrderInfoService;
import com.cdream.basic.classiccase.miaosha.service.SequenceInfoService;
import com.cdream.basic.classiccase.miaosha.service.StockLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author zs
 * @since 2021-07-17
 */
@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {
    @Autowired
    private SequenceInfoService sequenceInfoService;

    @Autowired
    private ItemService itemService;

    @Autowired
    private UserService userService;

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private StockLogService stockLogService;
    @Autowired
    private RocketMqTransProducer rocketMqTransProducer;

    /**
     * 基于数据库更新方案，瓶颈在于减库存，加销量全部落在一个itemId上，再更新记录时，会给表加上行锁[串行更新]
     * 秒杀活动下，一个商品访问量巨大，就会快速达到上限
     */
    @Override
    @Transactional
    public OrderInfo createOrderBySql(Integer userId, Integer itemId, Integer promoId, Integer amount, String stockLogId) throws RuntimeException {
        //1.校验下单状态,下单的商品是否存在，用户是否合法，购买数量是否正确
        //Item Item = itemService.getItemById(itemId);
        Item item = itemService.getById(itemId);

        if(item == null){
            throw new RuntimeException("商品信息不存在");
        }

        if(amount <= 0 || amount > 99){
            throw new RuntimeException("数量信息不正确");
        }

        //校验活动信息
        if(promoId != null){
            throw new RuntimeException("活动信息不正确");
        }

        //2.落单减库存
        boolean result = itemService.decreaseStock(itemId,amount);
        if(!result){
            throw new RuntimeException();
        }

        //3.订单入库
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setUserId(userId);
        orderInfo.setItemId(itemId);
        orderInfo.setAmount(amount);

        orderInfo.setItemPrice(item.getPrice());

        orderInfo.setPromoId(promoId);
        orderInfo.setOrderPrice(orderInfo.getItemPrice().multiply(new BigDecimal(amount)));

        //生成交易流水号,订单号
        orderInfo.setId(generateOrderNo());
        orderInfoMapper.insert(orderInfo);

        //加上商品的销量
        itemService.increaseSales(itemId,amount);

        //4.返回前端
        return orderInfo;
    }

    /**
     * 将库存信息放到redis里面，减库存操作在redis中完成即可
     * 通过mq发送消息异步扣减库存的方式存在严重一致性问题
     * 1.扣减操作失败，redis、mysql数据不一致
     * 2.下单失败无法正确回补库存
     */
    @Override
    @Transactional
    public OrderInfo createOrderByRedis(Integer userId, Integer itemId, Integer promoId, Integer amount, String stockLogId) throws RuntimeException {
        //1.校验下单状态,下单的商品是否存在，用户是否合法，购买数量是否正确
        //Item Item = itemService.getItemById(itemId);
        Item item = itemService.getByIdFromCache(itemId);

        if(item == null){
            throw new RuntimeException("商品信息不存在");
        }

        if(amount <= 0 || amount > 99){
            throw new RuntimeException("数量信息不正确");
        }

        //校验活动信息
        if(promoId != null){
            throw new RuntimeException("活动信息不正确");
        }

        //2.落单减库存
        boolean result = itemService.decreaseStockByRedis(itemId,amount);
        if(!result){
            throw new RuntimeException();
        }

        //3.订单入库
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setUserId(userId);
        orderInfo.setItemId(itemId);
        orderInfo.setAmount(amount);

        orderInfo.setItemPrice(item.getPrice());

        orderInfo.setPromoId(promoId);
        orderInfo.setOrderPrice(orderInfo.getItemPrice().multiply(new BigDecimal(amount)));

        //生成交易流水号,订单号
        orderInfo.setId(generateOrderNo());
        orderInfoMapper.insert(orderInfo);

        //加上商品的销量
        itemService.increaseSales(itemId,amount);

        //4.返回前端
        return orderInfo;
    }
    /**
     * 将库存信息放到redis里面，减库存操作在redis中完成即可
     */
    @Override
    @Transactional
    public OrderInfo createOrderByTransMq(Integer userId, Integer itemId, Integer promoId, Integer amount, String stockLogId) throws RuntimeException {
        //1.校验下单状态,下单的商品是否存在，用户是否合法，购买数量是否正确
        //Item Item = itemService.getItemById(itemId);
        Item item = itemService.getByIdFromCache(itemId);

        if(item == null){
            throw new RuntimeException("商品信息不存在");
        }

        if(amount <= 0 || amount > 99){
            throw new RuntimeException("数量信息不正确");
        }

        //校验活动信息
        // if(promoId != null){
        //     throw new RuntimeException("活动信息不正确");
        // }

        //2.落单减库存
        boolean result = itemService.decreaseStockForMq(itemId,amount);
        if(!result){
            throw new RuntimeException();
        }

        //3.订单入库
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setUserId(userId);
        orderInfo.setItemId(itemId);
        orderInfo.setAmount(amount);

        orderInfo.setItemPrice(item.getPrice());

        orderInfo.setPromoId(promoId);
        orderInfo.setOrderPrice(orderInfo.getItemPrice().multiply(new BigDecimal(amount)));

        //生成交易流水号,订单号
        orderInfo.setId(generateOrderNo());
        orderInfoMapper.insert(orderInfo);

        //加上商品的销量
        itemService.increaseSales(itemId,amount);

        // 设置库存流水状态
        StockLog stockLog = stockLogService.getById(stockLogId);
        if (stockLog==null){
            throw new RuntimeException("库存数据异常");
        }
        stockLog.setStatus(2);
        stockLogService.updateById(stockLog);
        //4.返回前端
        return orderInfo;
    }

    /**
     * 挂起当前事务，自己重开一个事务，外部失败不会影响这里，这里失败会影响外部
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public String generateOrderNo(){
        //订单号有16位
        StringBuilder stringBuilder = new StringBuilder();
        //前8位为时间信息，年月日
        LocalDateTime now = LocalDateTime.now();
        String nowDate = now.format(DateTimeFormatter.ISO_DATE).replace("-","");
        stringBuilder.append(nowDate);

        //中间6位为自增序列
        //获取当前sequence
        int sequence = 0;
        SequenceInfo sequenceInfo = sequenceInfoService.getSequenceByName("order_info");
        sequence = sequenceInfo.getCurrentValue();
        sequenceInfo.setCurrentValue(sequenceInfo.getCurrentValue() + sequenceInfo.getStep());
        sequenceInfoService.updateById(sequenceInfo);
        String sequenceStr = String.valueOf(sequence);
        for(int i = 0; i < 6-sequenceStr.length();i++){
            stringBuilder.append(0);
        }
        stringBuilder.append(sequenceStr);


        //最后2位为分库分表位,暂时写死
        stringBuilder.append("00");

        return stringBuilder.toString();
    }

    @Transactional(propagation = Propagation.NESTED)
    @Override
    public void realUpdate(Item item){
        OrderInfo orderInfo = getById("2018111800000000");
        orderInfo.setItemPrice(new BigDecimal("1"));
        updateById(orderInfo);
        System.out.println(1/0);
    }
}
